package com.xd.core.api.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xd.common.ascept.CheckSign;
import com.xd.common.config.RabbitConfig;
import com.xd.common.constant.SysConstant;
import com.xd.common.exception.XkdException;
import com.xd.common.util.*;
import com.xd.core.api.dto.*;
import com.xd.core.api.entity.RepaymentOrderInfo;
import com.xd.core.api.mapper.RepaymentOrderInfoMapper;
import com.xd.core.api.service.DLApiService;
import com.xd.core.api.vo.BalanceVo;
import com.xd.core.business.dto.InsuranceDto;
import com.xd.core.business.entity.Credit;
import com.xd.core.business.entity.RepaymentSchedule;
import com.xd.core.business.mapper.CreditMapper;
import com.xd.core.business.mapper.RepaymentScheduleMapper;
import com.xd.core.debt.entity.Debt;
import com.xd.core.debt.mapper.DebtMapper;
import com.xd.core.h5.dto.AdvancePaymentDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

@Api(tags = "小贷第三方API")
@RestController
@RequestMapping("/xdapi")
@Slf4j
public class DLApiController {

    @Resource
    private DLApiService dlApiService;
    @Autowired
    private RepaymentOrderInfoMapper repaymentOrderInfoMapper;
    @Autowired
    private RepaymentScheduleMapper repaymentScheduleMapper;
    @Autowired
    private DebtMapper debtMapper;
    @Value("${spring.profiles.active}")
    private String active;
    @Autowired
    private RedissonClient redissonClient;
    private static final String REPAY_LOCK_KEY = "lock_repay";
    private static final String DEBT_LOCK_KEY = "lock_debt";
    private static final int delay_time = 1000 * 60 * 60 * 4;
    @Autowired
    private CreditMapper creditMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;


    //    @ApiOperation("代付（放款）")
//    @PostMapping("/outAmount")
//    public void outAmount(@RequestBody OutAmountDto outAmountDto) throws Exception {
//        dlApiService.outAmount(outAmountDto);
//    }
    public static void main(String[] args) {
        String phone = AESUtil.encrypt("13982912591", "bd482fcff91d47cc");
        System.out.println(phone);

    }

    /*
     * 代扣需要
     * */
    @PreAuthorize("hasAnyAuthority('sys:Lending-WithholdBinding')")
    @CheckSign
    @ApiOperation("预绑卡")
    @PostMapping("/readySign")
    public String readySign(@RequestBody ReadySignDto readySignDto) throws Exception {
        if (!active.equals("prd"))
            throw new XkdException("测试环境不允许操作");
        return dlApiService.readySign(readySignDto);
    }

    @PreAuthorize("hasAnyAuthority('sys:Lending-WithholdBinding')")
    @CheckSign
    @ApiOperation("确认绑卡")
    @PostMapping("/confirmSign")
    public void confirmSign(@RequestBody ConfirmSignDto confirmSignDto) throws Exception {
        dlApiService.confirmSign(confirmSignDto);
    }

    @ApiOperation("解绑")
    @PostMapping("/ABolishBind")
    public void ABolishBind(String uniqueCode) throws Exception {
        dlApiService.ABolishBind(uniqueCode);
    }

    //协议支付分账
    //手动代扣  每一期 或者 提前还款
    @PreAuthorize("hasAnyAuthority('sys:BillList-ManualDeduction','sys:AccountsReceivableList-ManualDeduction'," +
            "'sys:Lending-ManualWithholdings','sys:Lending-ManualWithholding')")
    @CheckSign
    @Transactional(rollbackFor = Exception.class)
    @ApiOperation("直接支付(手动代扣)")
    @PostMapping("/singlePay")
    public void singlePay(@RequestBody SinglePayDto singlePayDto) throws Exception {

        String type = singlePayDto.getType();
        if (type == null)
            throw new XkdException("type不能为空");
        if (!type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW) && !type.equals(SysConstant.DEDUCT_MONEY_TYPE_FIVE))
            throw new XkdException("type类型错误");
        String lockKey = null;
        if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW)) {
            lockKey = REPAY_LOCK_KEY + singlePayDto.getOrderId();
        } else {
            lockKey = DEBT_LOCK_KEY + singlePayDto.getDebtId();
        }
        RLock lock = redissonClient.getLock(lockKey);

        try {
            log.info("尝试获取锁");
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                log.info("成功获取锁");
                Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, singlePayDto.getIncomingId()));
                Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getId, singlePayDto.getDebtId()));
                Integer outId = debt.getOutId();
//                if (debt.getStatus().equals(SysConstant.ORDER_STATUS_FOUR) || debt.getStatus().equals(SysConstant.ORDER_STATUS_FIVE))
//                    throw new XkdException("该订单已退保");
                if (debt.getIsWithhold().equals("1"))
                    throw new XkdException("该债权转让后未选择代收付");
                RepaymentOrderInfo repaymentOrderInfo = new RepaymentOrderInfo();
                if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW)) {
                    if(debt.getIsEarly().equals("1"))
                        throw new XkdException("已申请提前还款，无法代扣本期");
                    //查询是否有申请中的还款
                    Integer i = repaymentOrderInfoMapper.getRepayOrderStatus(singlePayDto.getOrderId());
                    if (i > 0)
                        throw new XkdException("有申请中的还款或已完成还款");
                    //判断以前是否有未还或者逾期的账单
                    Integer j = repaymentScheduleMapper.getIsUntreated(outId, singlePayDto.getOrderId());
                    if (j > 0)
                        throw new XkdException("前面有未处理的账单");
                    RepaymentSchedule repaymentSchedule = repaymentScheduleMapper.selectById(singlePayDto.getOrderId());
                    if (repaymentSchedule.getStatus().equals(SysConstant.REPAYMENT_STATUS_TOW))
                        throw new XkdException("本期已还");
                    if (repaymentSchedule.getRepayTimes() >= 2)
                        throw new XkdException("手动代扣每日限制2次");
                    repaymentOrderInfo.setOrderId(singlePayDto.getOrderId());
                    //支付金额要加上逾期利息 罚息
                    Integer amount1 = repaymentSchedule.getAmount().multiply(new BigDecimal(100)).intValue();
                    Integer amount2 = debt.getOverdueInterest().add(debt.getDefaultInterestTodo()).multiply(new BigDecimal(100)).intValue();
                    int amount = amount1 + amount2;
                    repaymentOrderInfo.setAmount(amount);
                    singlePayDto.setAmount(String.valueOf(amount));
                } else {
                    //查询债权表是否有申请中的代扣
                    Integer i = repaymentOrderInfoMapper.getDebtOrderStatus(singlePayDto.getDebtId());
                    if (i > 0)
                        throw new XkdException("有申请中的还款或已完成还款");
                    if (debt.getStatus().equals(2))
                        throw new XkdException("已结清");
                    if (debt.getWithholdingTimes() >= 2)
                        throw new XkdException("手动代扣每日限制2次");
                    int amount = debt.getEarlyAmount().multiply(new BigDecimal(100)).intValue();
                    repaymentOrderInfo.setAmount(amount);
                    singlePayDto.setAmount(String.valueOf(amount));
                }
                String repayOrder = OrderNumUtil.getRepayOrder();
                repaymentOrderInfo.setDebtId(singlePayDto.getDebtId());
                repaymentOrderInfo.setOrderNo(repayOrder);
                repaymentOrderInfo.setType(singlePayDto.getType());
//                repaymentOrderInfo.setAmount(Integer.parseInt(singlePayDto.getAmount()));
                repaymentOrderInfo.setCompanyId(singlePayDto.getCompanyId());
                repaymentOrderInfo.setRepayName(credit.getRepayName());
                repaymentOrderInfo.setRepayAccount(credit.getRepayAccount());
                repaymentOrderInfo.setRepayBank(credit.getRepayBank());
                repaymentOrderInfo.setIncomingId(singlePayDto.getIncomingId());
                repaymentOrderInfo.setOutId(outId);
                //插入订单
                repaymentOrderInfoMapper.insert(repaymentOrderInfo);
                singlePayDto.setOrderNo(repayOrder);
                dlApiService.singlePay(singlePayDto, repaymentOrderInfo);
                if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW))
                    repaymentScheduleMapper.updateRepayTimes(singlePayDto.getOrderId());//增加代扣次数
            } else {
                throw new XkdException("服务器繁忙请稍后再试");
            }
        } catch (Exception e) {
            log.error("手动代扣失败，请求参数: {}, 锁键: {}, 异常信息: {}", singlePayDto, lockKey, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("手动代扣任务释放锁，锁键: {}", lockKey);
            }
        }
    }

//    @CheckSign
//    @ApiOperation("H5预付款代扣")
//    @PostMapping("/advancePayment")
//    public void advancePayment(@RequestBody InsuranceDto dto) throws Exception {
//        dlApiService.advancePayment(confirmSignDto);
//    }

    @PreAuthorize("hasAnyAuthority('sys:BillList-OfflineRepayment','sys:RepaymentManagement-OfflineRepayment'," +
            "'sys:Lending-OfflineRepayments','sys:Lending-OfflineRepayment')")
    @CheckSign
    @ApiOperation("线下转账（创建订单）")
    @PostMapping("/offlinePay")
    public void offlinePay(@RequestBody OfflinePayDto offlinePayDto) throws Exception {
        String type = offlinePayDto.getType();
        if (type == null)
            throw new XkdException("type不能为空");
        if (!type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE) && !type.equals(SysConstant.DEDUCT_MONEY_TYPE_SIX))
            throw new XkdException("type类型错误");
        String lockKey = null;
        if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE)) {
            lockKey = REPAY_LOCK_KEY + offlinePayDto.getRepayId();
        } else {
            lockKey = DEBT_LOCK_KEY + offlinePayDto.getDebtId();
        }
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getId, offlinePayDto.getDebtId()));
                Integer outId = debt.getOutId();
//                if (debt.getStatus().equals(SysConstant.ORDER_STATUS_FOUR) || debt.getStatus().equals(SysConstant.ORDER_STATUS_FIVE))
//                    throw new XkdException("该订单已退保");
                if (debt.getIsWithhold().equals("1"))
                    throw new XkdException("该债权转让后未选择代收付");
                Integer i = null;
                if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE)) {
                    if(debt.getIsEarly().equals("1"))
                        throw new XkdException("已申请提前还款，无法代扣本期");
                    //查询是否有申请中的代扣
                    i = repaymentOrderInfoMapper.getRepayOrderStatus(offlinePayDto.getRepayId());
                    //判断以前是否有未还或者逾期的账单
                    Integer j = repaymentScheduleMapper.getIsUntreated(outId, offlinePayDto.getRepayId());
                    if (j > 0)
                        throw new XkdException("前面有未处理的账单");
                } else {
                    //查询债权表是否有申请中的代扣
                    i = repaymentOrderInfoMapper.getDebtOrderStatus(offlinePayDto.getDebtId());
                }
                if (i > 0)
                    throw new XkdException("有申请中的还款或已完成还款");
                String s = dlApiService.offlinePay(offlinePayDto);
                //发送延迟消息 4小时
                DelayOfflinePayDto delayOfflinePayDto = new DelayOfflinePayDto();
                delayOfflinePayDto.setRepaymentOrderNo(s);
                delayOfflinePayDto.setOrderId(offlinePayDto.getRepayId());
                delayOfflinePayDto.setDebtId(offlinePayDto.getDebtId());
                delayOfflinePayDto.setType(offlinePayDto.getType());
                sendDelayOfflinePayMessage(delayOfflinePayDto, delay_time);
            } else {
                throw new XkdException("服务器繁忙，请勿重复提交");
            }
        } catch (Exception e) {
            log.error("线下转账失败，请求参数: {}, 锁键: {}, 异常信息: {}", offlinePayDto, lockKey, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("手动代扣任务释放锁，锁键: {}", lockKey);
            }
        }
    }

    //payPass：通道类型
    //平台专用--放款账户
    @ApiOperation("宝付余额")
    @PostMapping("/baofuBalance")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "payPass", value = "通道类型（1-通道一，2-通道二）", dataType = "string")
    })
    public BalanceVo baofuBalance(String payPass) throws Exception {
        return dlApiService.baofuBalance(payPass);
    }

    //传渠道二银行卡ID
    @CheckSign
    @ApiOperation("放款账户-渠道二提现(平台)")
    @PostMapping("/towBaofuwithdrawDeposit")
    public void towBaofuwithdrawDeposit(@RequestBody TowWithdrawDepositDto towWithdrawDepositDto) throws Exception {
        dlApiService.towBaofuwithdrawDeposit(towWithdrawDepositDto);
    }

    //传渠道二银行卡ID
    @CheckSign
    @ApiOperation("渠道二提现(记账)")
    @PostMapping("/HKtowBaofuwithdrawDeposit")
    public void HKtowBaofuwithdrawDeposit(@RequestBody TowWithdrawDepositDto towWithdrawDepositDto) throws Exception {
        dlApiService.HKtowBaofuwithdrawDeposit(towWithdrawDepositDto);
    }

    private void sendDelayOfflinePayMessage(DelayOfflinePayDto msg, int delayMillis) {
        log.info("发送线下还款订单延迟消息");
        rabbitTemplate.convertAndSend(
                RabbitConfig.DELAY_EXCHANGE,
                RabbitConfig.DELAY_ROUTING_KEY,
                msg,
                message -> {
                    message.getMessageProperties().setDelay(delayMillis);
                    return message;
                });
    }


    @ApiOperation("预付款-手动预付款代扣")
    @PostMapping("/h5SinglePay")
    public void h5SinglePay(@RequestBody AdvancePaymentDto advancePaymentDto) throws Exception {
        dlApiService.h5SinglePay(advancePaymentDto);
    }

    @PreAuthorize("hasAnyAuthority('sys:Lending-ViewReceipts')")
    @ApiOperation("放款凭证")
    @PostMapping("/VDownload")
    public String VDownload(@RequestBody VDownloadDto vDownloadDto) throws Exception {
        return dlApiService.VDownload(vDownloadDto);
    }

}


